package handler

import (
	"context"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"math"
	"time"

	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/modules/configuration/configuration/attribute/attrtype"
	"kop/modules/item/consts"
	"kop/modules/role/configuration/level"
	"kop/modules/stage/configuration"
	"kop/modules/stage/configuration/event"
	"kop/modules/stage/entity"
	"kop/modules/stage/models"
	"kop/pb"
	"kop/pb/service"
	"kop/util/rand"

	awardM "kop/modules/award/models"
	battleM "kop/modules/battle/models"
	captainC "kop/modules/captain/client"
	moneyC "kop/modules/money/client"
	roleC "kop/modules/role/client"
)

type StageServiceHandler struct {
	context2.Context
}

func (h *StageServiceHandler) GetStage(ctx context.Context, _ *empty.Empty, resp *pb.Stage) error {

	var stage = models.FirstOrCreate(database.DB, h.GetRoleID(ctx), h.GetServerID(ctx))

	if stage.Power == 0 || time.Now().Sub(stage.UpdatedAt).Seconds() > 5 {

		stage.Power = captainC.SumPower(h.GetRoleID(ctx))
		models.SaveStage(database.DB, &stage)
	}

	*resp = *buildPbStage(stage)
	return nil
}

func (h *StageServiceHandler) Next(_ context.Context, req *pb.Stage, resp *service.StageNextResp) error {

	var stage = models.GetStage(database.DB, req.RoleID)
	var conf, _ = configuration.GetByStageID(stage.StageID)
	stage.StageID += 1
	stage.Step = 0

	var _, err = configuration.GetByStageID(stage.StageID)
	if err != nil {
		return err
	}

	models.SaveStage(database.DB, &stage)

	var role = roleC.GetRole(req.RoleID)

	resp.Stage = buildPbStage(stage)
	resp.Award = awardM.RandAward(role, conf.AwardID)
	return awardM.SendAward(role, resp.Award)
}

func (h *StageServiceHandler) Walk(ctx context.Context, _ *empty.Empty, resp *pb.WalkReply) error {

	var roleID = h.GetRoleID(ctx)
	var stage = models.GetStage(database.DB, roleID)

	var conf, _ = configuration.GetByStageID(stage.StageID)

	if stage.Step >= conf.MaxStep()-1 {
		return errors.New("Next is Boss")
	}

	var coin = int64(float64(conf.CostCoin) * costRate(stage, conf))
	var money, err = moneyC.SubCoin(ctx, coin)
	if err != nil {
		return err
	}

	stage.Step += 1

	var role = roleC.GetRole(roleID)
	if stage.Step%5 == 0 && stage.Step != conf.MaxStep() {
		// 5步1个事件
		//stage.EventID = 1

		var events []rand.Value
		for _, c := range event.Configs {
			events = append(events, c)
		}

		var eventConf = rand.WithWeight(events, 1)[0].(event.Conf)

		resp.EventID = eventConf.EventID

		if eventConf.EventType != event.Type1 {
			stage.EventID = eventConf.EventID

		} else {

			resp.Award = awardM.RandAward(role, eventConf.AwardID[0])
		}
	} else {

		resp.Award = &pb.Award{}
		resp.Award.AddRawItem(&pb.RawItem{ItemID: consts.Gold, Number: conf.Gold})
	}

	models.SaveStage(database.DB, &stage)
	resp.Stage = buildPbStage(stage)

	if resp.Award == nil {
		resp.Award = &pb.Award{}
	}

	resp.Award.Money = money
	return awardM.SendAward(role, resp.Award)
}

func (h *StageServiceHandler) Event2(ctx context.Context, req *wrappers.Int32Value, resp *pb.StageEventResp) error {

	if req.Value < 0 || req.Value > 3 {
		return errors.ParamError
	}

	var roleID = h.GetRoleID(ctx)
	var stage = models.GetStage(database.DB, roleID)
	if stage.EventID == 0 {
		return errors.ParamError
	}

	var eventConf, err = event.GetEventByID(stage.EventID)
	if err != nil {
		return err
	}

	if eventConf.EventType != event.Type2 {
		return errors.ParamError
	}

	var awardID = eventConf.AwardID[int(req.Value)]

	var conf, _ = configuration.GetByStageID(stage.StageID)

	var role = roleC.GetRole(roleID)

	resp.Award = awardM.RandAward(role, awardID)
	if eventConf.AwardCoin[int(req.Value)] > 0 {

		resp.Award.Coin += int64(float64(conf.CostCoin) * eventConf.AwardCoin[int(req.Value)])

	} else if eventConf.AwardGold[int(req.Value)] > 0 {

		for _, v := range resp.Award.AwardItems {
			if v.RawItem.ItemID == consts.Gold {
				v.RawItem.Number += int32(float64(conf.Gold) * eventConf.AwardGold[int(req.Value)])
			}
		}
	}

	// update eventID = 0  and send award
	stage.EventID = 0
	models.SaveStage(database.DB, &stage)

	return awardM.SendAward(role, resp.Award)
}

func (h *StageServiceHandler) Event3(ctx context.Context, req *wrappers.Int32Value, resp *pb.StageEventResp) error {

	var roleID = h.GetRoleID(ctx)
	var stage = models.GetStage(database.DB, roleID)

	if stage.EventID == 0 {
		return errors.ParamError
	}

	var role = roleC.GetRole(roleID)

	var eventConf, err = event.GetEventByID(stage.EventID)
	if err != nil {
		return err
	}

	if eventConf.EventType != event.Type3 {
		return errors.New("事件类型错误")
	}

	captain := captainC.GetCaptain(roleID, req.Value)

	if eventConf.NeedCamp > 0 && captain.Camp != eventConf.NeedCamp {
		resp.Success = false
	} else if eventConf.NeedSex > 0 && captain.Sex != eventConf.NeedSex {
		resp.Success = false
	} else {
		resp.Success = true
	}

	roleConf, err := level.GetByRoleLevel(role.Level)
	if err != nil {
		return err
	}

	if resp.Success {
		switch eventConf.NeedAttribute {
		case attrtype.TypeINT:

			var rate = float32(captain.Attribute.INT) / float32(roleConf.StandardINT)
			if rate < 0.2 {
				rate = 0.2
			}

			resp.Success = rand.IsWin(rate)

		case attrtype.TypeCOR:

			var rate = float32(captain.Attribute.COR) / float32(roleConf.StandardCOR)
			if rate < 0.2 {
				rate = 0.2
			}

			resp.Success = rand.IsWin(rate)

		case attrtype.TypeCHR:

			var rate = float32(captain.Attribute.CHR) / float32(roleConf.StandardCHR)
			if rate < 0.2 {
				rate = 0.2
			}

			resp.Success = rand.IsWin(rate)
		}
	}

	conf, err := configuration.GetByStageID(stage.StageID)
	if err != nil {
		return err
	}

	var awardID int32

	if resp.Success {
		awardID = eventConf.AwardID[0]
	} else {
		awardID = eventConf.AwardID[1]
	}

	resp.Award = awardM.RandAward(role, awardID)

	if resp.Success {
		// 奖励的数量 还要再乘配置里的比率
		resp.Award.Coin += int64(float64(conf.CostCoin) * eventConf.AwardCoin[0])

		for _, v := range resp.Award.AwardItems {
			if v.RawItem.ItemID == consts.Gold {
				v.RawItem.Number += int32(float64(conf.Gold) * eventConf.AwardGold[0])
			}
		}

	} else {
		resp.Award.Coin += int64(float64(conf.CostCoin) * eventConf.AwardCoin[1])

		for _, v := range resp.Award.AwardItems {
			if v.RawItem.ItemID == consts.Gold {
				v.RawItem.Number += int32(float64(conf.Gold) * eventConf.AwardGold[1])
			}
		}
	}

	stage.EventID = 0
	models.SaveStage(database.DB, &stage)

	return awardM.SendAward(role, resp.Award)
}

func (h *StageServiceHandler) Ranking(_ context.Context, req *wrappers.Int32Value, resp *service.RankingPointsList) error {

	var data []entity.Stage

	database.DB.Where("server_id = ? AND stage_id > 1", req.Value).Order("stage_id DESC, updated_at ASC").Limit(200).Find(&data)

	resp.Ranking = make([]*service.RankingPoints, 0, len(data))
	for _, v := range data {
		resp.Ranking = append(resp.Ranking, &service.RankingPoints{RoleID: v.RoleID, Points: int64(v.StageID)})
	}

	return nil
}

func (h *StageServiceHandler) MyRanking(ctx context.Context, _ *empty.Empty, resp *pb.RankingNumber) (err error) {

	var stage = models.FirstOrCreate(database.DB, h.GetRoleID(ctx), h.GetServerID(ctx))

	var number int32
	database.DB.Model(&stage).Where("server_id = ? AND stage_id > ?", h.GetServerID(ctx), stage.StageID).Count(&number)

	resp.Points = int64(stage.StageID)
	resp.Number = number + 1
	return nil
}

func costRate(stage entity.Stage, conf configuration.Conf) float64 {

	//【当前阶段副本实力】= 当前副本实力 * 当前阶段实力增长比例
	//【实力比例】= 当前已招募船长实力 / 当前阶段副本实力
	//※ 已流放船长不计算在内
	//实力比例 >= 1,【当前阶段消耗比例】= 1 - (【实力比例】-1)/4
	//※ 当前阶段消耗比例最小值0.5
	//实力比例 < 1,【当前阶段消耗比例】= 1 + ( POWER (2-【实力比例】,8)-1)/7
	//※ 当前阶段消耗比例最大值25
	//【当前阶段实际银币消耗】= 当前副本每步移动银币消耗 * 当前阶段消耗比例

	var costRate = 1.0
	var powerRate = float64(stage.Power) / (float64(conf.Power) * conf.CoinRates[int(stage.Step/5)])
	if powerRate >= 1 {
		costRate = 1 - (powerRate-1)/4
		costRate = math.Max(costRate, 0.5)
	} else {
		costRate = 1 + (math.Pow(2-powerRate, 8)-1)/7
		powerRate = math.Min(costRate, 25)
	}

	return costRate
}

func buildPbStage(stage entity.Stage) *pb.Stage {

	var conf, err = configuration.GetByStageID(stage.StageID)
	if err != nil {
		panic(err)
	}

	_, err = configuration.GetByStageID(stage.StageID + 1)
	// 这里不能return哦

	var costRate = costRate(stage, conf)

	var mess = pb.Stage{
		RoleID:    stage.RoleID,
		StageID:   stage.StageID,
		Step:      stage.Step, // 步数
		MaxStep:   conf.MaxStep(),
		EventID:   stage.EventID,
		WalkCoin:  int64(float64(conf.CostCoin) * costRate),
		WalkGold:  conf.Gold,
		NeedLevel: conf.NeedLevel,
		HaveNext:  err == nil,
	}

	if mess.Step >= mess.MaxStep-1 {

		mess.Boss, err = battleM.GetNpc(conf.BossID, conf.BossLevel)
		if err != nil {
			panic(err)
		}
	}

	if costRate < 0.7 {
		mess.CostLevel = 1
	} else if costRate < 0.9 {
		mess.CostLevel = 2
	} else if costRate < 1.5 {
		mess.CostLevel = 3
	} else if costRate < 6 {
		mess.CostLevel = 4
	} else {
		mess.CostLevel = 5
	}

	return &mess
}
